//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#ifndef __ELASTOS_PAGING_H__
#define __ELASTOS_PAGING_H__

// Page macros
#define PAGE_SHIFT_4K               (12)
#define PAGE_SHIFT_4M               (22)
#define PAGEDIR_SHIFT               (22)

#define PAGE_SHIFT                  PAGE_SHIFT_4K
#define PAGE_TO_PAGEDIR_SHIFT       (PAGEDIR_SHIFT - PAGE_SHIFT)

#define PAGE_SIZE_4K                (1u << PAGE_SHIFT_4K)
#define PAGE_SIZE_4M                (1u << PAGE_SHIFT_4M)

#define PAGE_SIZE                   PAGE_SIZE_4K
#define PAGEDIR_SIZE                (1u << PAGEDIR_SHIFT)

#define PAGE_MASK_4K                (~(PAGE_SIZE_4K - 1))
#define PAGE_MASK_4M                (~(PAGE_SIZE_4M - 1))
#define PAGE_MASK                   PAGE_MASK_4K

#define PAGE_OFFSET_4K(va)          ((uint32_t)(va) & (PAGE_SIZE_4K - 1))
#define PAGE_OFFSET_4M(va)          ((uint32_t)(va) & (PAGE_SIZE_4M - 1))

#define PAGE_OFFSET(va)             PAGE_OFFSET_4K(va)
#define PAGETAB_INDEX(va)           (((uint32_t)(va) >> PAGE_SHIFT) & 0x3ff)
#define PAGEDIR_INDEX(va)           ((uint32_t)(va) >> PAGEDIR_SHIFT)

#define PD_MASK                     0xffc00000
#define PT_MASK                     0xfffff000
#define PD_INDEX(va)                ((va) >> PAGEDIR_SHIFT)
#define PT_INDEX(va)                (((va) >> PAGE_SHIFT) & 0x3ff)

#define PAGETAB_BASEADDR_MASK       0xfffff000

// Page-Directory Entry and Page-Table Entry flags
enum {
    PageEntryFlag_Present       = __32BIT(0),
    PageEntryFlag_RW            = __32BIT(1),   // Read/Write
                                                //  (0 indicates read only)
    PageEntryFlag_User          = __32BIT(2),   // User/Supervisor
    PageEntryFlag_PWT           = __32BIT(3),   // Write-through
    PageEntryFlag_PCD           = __32BIT(4),   // Cache disabled
    PageEntryFlag_Accessed      = __32BIT(5),
    PageEntryFlag_Dirty         = __32BIT(6),   // Only in 4k PTE and 4M PDE
    PageEntryFlag_PageSize      = __32BIT(7),   // Only in PDE
                                                //  (1 indicates 4 MBytes)
    PageEntryFlag_Global        = __32BIT(8),   // Global page
    PageEntryFlag_Avail_1       = __32BIT(9),   // Available for system
    PageEntryFlag_Avail_2       = __32BIT(10),  // programmer's use
    PageEntryFlag_Avail_3       = __32BIT(11),
};

// Convertion
#define NUMBEROF_PAGES(size)        ((size) >> PAGE_SHIFT)
#define PHYSADDR_TO_PAGENO(pa)      ((pa) >> PAGE_SHIFT)
#define VIRTADDR_TO_PAGENO(va)      PHYSADDR_TO_PAGENO(PHYSADDR(va))
#define PAGENO_TO_PHYSADDR(no)      ((no) << PAGE_SHIFT)
#define PAGENO_TO_VIRTADDR(no)      VIRTADDR(PAGENO_TO_PHYSADDR(no))
#define VIRTPAGENO_TO_VIRTADDR(no)  ((no) << PAGE_SHIFT)

#define NUMBEROF_PAGETABS(size)     ((size) >> PAGEDIR_SHIFT)
#define PHYSADDR_TO_PAGETABNO(pa)   ((pa) >> PAGEDIR_SHIFT)
#define VIRTADDR_TO_PAGETABNO(va)   PHYSADDR_TO_PAGETABNO(PHYSADDR(va))
#define PAGETABNO_TO_PHYSADDR(no)   ((no) << PAGEDIR_SHIFT)
#define PAGETABNO_TO_VIRTADDR(no)   VIRTADDR(PAGETABNO_TO_PHYSADDR(no))

#define PAGENO_TO_PAGETABNO(no)     ((no) >> PAGE_TO_PAGEDIR_SHIFT)

const uint_t c_uMaxNumberOfPages    = NUMBEROF_PAGES(0xffffffff) + 1;

#define IS_VALID_PAGENO(no)         ((no) < c_uMaxNumberOfPages)

const uint_t c_uKernelBasePageNo    =
        PHYSADDR_TO_PAGENO((physaddr_t)KERNEL_BASE);

const uint_t c_uNumberOfKernelPages =
        c_uMaxNumberOfPages - c_uKernelBasePageNo;

const uint_t c_uKernelPhysBasePageNo =
        PHYSADDR_TO_PAGENO(bsp.paKernelPhysBase);

#define PHYSPAGENO_TO_VIRTPAGENO(no) \
        ((no) + c_uKernelBasePageNo - c_uKernelPhysBasePageNo)

#define VIRTPAGENO_TO_PHYSPAGENO(no) \
        ((no) - c_uKernelBasePageNo + c_uKernelPhysBasePageNo)

typedef struct PageDirEntry {
    uint32_t uValue;
} PageDirEntry;

typedef struct PageTabEntry {
    uint32_t uValue;
} PageTabEntry;

const size_t c_uSizeOfPageDir           = PAGE_SIZE;
const size_t c_uSizeOfPageTab           = PAGE_SIZE;

// Number of entries
const uint_t c_uNumberOfPageDirEntries  =
        c_uSizeOfPageDir / sizeof(PageDirEntry);
const uint_t c_uNumberOfPageTabEntries  =
        c_uSizeOfPageTab / sizeof(PageTabEntry);

const uint_t c_cPageDirEntries = c_uNumberOfPageDirEntries;
const uint_t c_cPageTabEntries = c_uNumberOfPageTabEntries;

EXTERN PageDirEntry g_initPageDir[c_uNumberOfPageDirEntries];

INLINE void FlushAllTlbs()
{
    ASM("movl   %%cr3, %%eax;"
        "movl   %%eax, %%cr3;"
        : : :"eax", "memory");
}

INLINE void FlushTlb(virtaddr_t va)
{
    ASM("invlpg     %0\n"
        : :"m"(*(char *)va));
}

INLINE void FlushInstructionCache(uint_t uBase, MemorySize limit)
{
}

#endif //__ELASTOS_PAGING_H__
